package util;

import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;
import java.io.FileOutputStream;
import java.util.Map;
import java.util.HashMap;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;

public class DtoGenerator {

	public static String classNameSuffix = "DTO";

	/**
	 * Take an input package directory and an output generated-classes
	 * directory, and convert all the classes in the first into JDK 1.4 beans in
	 * the second.
	 */
	@SuppressWarnings("unchecked")
	public static void main(String... argv) {
		String pckgsrc = "domain";
		String pckgdst = "mtt.us.dto";
		String outPath = "../mttus-client/src/";

		// Create the catalog directory if it doesn't exist.
		File outDir = new File(outPath);
		if (!outDir.exists()) {
			outDir.mkdirs();
		}

		try {
			Class[] classes = ClassUtil.getClasses(pckgsrc);

			// Map from original names to generated names
			Map<String, String> generatedClassNameMap = new HashMap<String, String>();

			for (Class clazz : classes) {
				generatedClassNameMap.put(clazz.getName(), pckgdst + "."
						+ clazz.getSimpleName() + classNameSuffix);
			}

			for (Class clazz : classes) {
				System.out.println("Next class to generate: " + clazz);
				generateJdk14Class(clazz, outDir, pckgdst,
						generatedClassNameMap);
			}

		} catch (Exception ex) {
			System.out.println("Could not generate JDK 1.4 beans: "
					+ ex.getMessage());
			ex.printStackTrace();
			System.exit(1);
		}
	}

	/**
	 * Generate a JDK 1.4 class for the given source class. Yes, this should
	 * probably use Freemarker or something.....
	 */
	@SuppressWarnings("unchecked")
	private static void generateJdk14Class(Class clazz, File outDir,
			String pckgdst, Map<String, String> generatedClassNameMap)
			throws IOException {

		String classNamePath = generatedClassNameMap.get(clazz.getName())
				.replace('.', File.separatorChar);

		File outFile = new File(outDir, File.separator + classNamePath
				+ ".java");

		outFile.getParentFile().mkdirs();

		PrintWriter out = new PrintWriter(new FileOutputStream(outFile));

		// TODO: handle inheritance

		out.println("package " + pckgdst + ";\n");
		out.println("public class " + clazz.getSimpleName() + classNameSuffix
				+ " extends net.sf.hibernate4gwt.pojo.gwt.LazyGwtPojo {\n");

		out.println("\tpublic " + clazz.getSimpleName() + classNameSuffix
				+ "() {}\n");

		// Get all fields of the class
		for (Field field : clazz.getDeclaredFields()) {

			String name = field.getName();
			Type type = field.getGenericType();
			String typeName = field.getType().getName();

			if (typeName.startsWith("java.")) {
				// skip over classes that aren't emulated by GWT
				String emulSourceFileName = "com/google/gwt/emul/"
						+ typeName.replace(".", "/") + ".java";
				URL resource = Thread.currentThread().getContextClassLoader()
						.getResource(emulSourceFileName);
				if (resource == null) {
					System.out.println("Could not find resource "
							+ emulSourceFileName + "; skipping field");
					continue;
				}
			}

			if (generatedClassNameMap.containsKey(typeName)) {
				typeName = generatedClassNameMap.get(typeName);
			}
			// Add gwt.typeArgs
			if (type instanceof ParameterizedType) {
				ParameterizedType parameterizedType = (ParameterizedType) type;
				Type[] generics = parameterizedType.getActualTypeArguments();
				String generic = "";
				for (int i = 0; i < generics.length; i++) {
					if (generics[i] instanceof Class) {
						if (generic.length() > 0)
							generic += ", ";
						Class classG = (Class) generics[i];
						if (generatedClassNameMap.containsKey(classG.getName()))
							generic += generatedClassNameMap.get(classG
									.getName());
						else
							generic += classG.getName();
					}
				}
				out.println("\t/**\n\t * @gwt.typeArgs <" + generic.trim()
						+ ">\n\t */");
			}
			if (name.equals("serialVersionUID")) {
				try {
					field.setAccessible(true);
					out.println("\tprivate static final " + typeName + " " + name
							+ " = " + field.getLong(field) + "L;\n");
				} catch (IllegalArgumentException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				continue;
			} else
				out.println("\tprivate " + typeName + " " + name + ";\n");

			String camelName = name.substring(0, 1).toUpperCase()
					+ name.substring(1);

			out.println("\tpublic " + typeName + " get" + camelName
					+ "() { return " + name + "; }");

			if (name.equals("serialVersionUID"))
				out.println("\tpublic void set" + camelName + "(" + typeName
						+ " " + name + ") { " + name + " = " + name + "; }\n");
			else
				out.println("\tpublic void set" + camelName + "(" + typeName
						+ " " + name + ") { this." + name + " = " + name
						+ "; }\n");

			out.println();
		}

		out.println("}");

		out.close();
	}
}
